/**
 * Function Information for dragonscript
 * Copyright (C) 2009 Hajime Yoshimori <RoarLudros@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <sstream>
#include <list>
#include <string>

#include "DragonFuncInfo"
#include "DragonMessage"
#include "DragonExcept"
#include "DragonChar"

dragon::DragonFuncInfo::DragonFuncInfo(std::wstring command_sentence, unsigned int priority, std::wstring command_name)
  : _priority(priority) {

  Analyze(command_sentence);
  if(command_name != L"") {
	_command_name = command_name;
  }
}

dragon::DragonFuncInfo::DragonFuncInfo(DragonSentence& compiled_object, unsigned int priority, std::wstring command_name)
  : _priority(priority), _master_parts(compiled_object) {

  MakeName();
  if(command_name != L"") {
	_command_name = command_name;
  }
}

namespace dragon {
  #define _(a) (a)
  const dragon::DragonMessage msgEmptyCommandSentence =
	dragon::DragonMessage(dragon::DragonMessage::error,
						  _("Command grammer sentence must not be empty."),
						  _("I can\'t understand that grammer sentence."));
  
  const dragon::DragonMessage msgNoClosingParenthesis =
	dragon::DragonMessage(dragon::DragonMessage::error,
						  _("There is no closing \")\"."),
						  _("This is wrong because there is no closing \")\"."));
  
  const dragon::DragonMessage msgNoClosingBraces =
    dragon::DragonMessage(dragon::DragonMessage::error,
						  _("There is no closing \"}\"."),
						  _("This is wrong because there is no closing \"}\"."));
  
  const dragon::DragonMessage msgNoClosingSquareBracket =
    dragon::DragonMessage(dragon::DragonMessage::error,
						  _("There is no closing \"]\" or Single selection."),
						  _("This is wrong because there is no closing \"]\" or single selection."));
						  
  const dragon::DragonMessage msgSingleSelection =
    dragon::DragonMessage(dragon::DragonMessage::error,
						  _("This is single selection."),
						  _("This is wrong because this is single selection."));
  #undef _
}

void dragon::DragonFuncInfo::PartialAnalyze(dragon::DragonSentence* sp, std::wstring command_sentence) {
  std::wistringstream iws;
  
  iws.str(command_sentence);
  
  bool escaped = false;
  while(iws) {
	dragon::DragonChar wch = iws.get();
	
	switch(wch) {
	  /* Escape */
	  case L'\\':
		if(!escaped) {
		  escaped = true;
		} else {
		  escaped = false;
		  DragonFuncPart tp(L"\\");
		  sp->push_back(tp);
		}
		break;
		
	  /* Omittable (.A.) */
	  case L'(':
		if(escaped) {
		  escaped = false;
		  DragonFuncPart tp(L"(");
		  sp->push_back(tp);
		} else {
		  std::wostringstream ows;
		  std::wstring tws;
		  unsigned int cnt = 1;
		  bool e = false;
		  do {
			e = false;
			cnt--;
			std::getline(iws, tws, L')');
			if(iws.eof()) {
			  throw DragonExcept(&msgNoClosingParenthesis);
			}
			if(tws[tws.length() - 1] == L'\\') {
			  e = true;
			}
			for(std::wstring::size_type i = tws.find(L"(", 0); i != std::wstring::npos; i = tws.find(L"(", i + 1)) {
			  if(i == 0 || tws[i - 1] != L'\\') {
				cnt++;
			  }
			}
			ows << tws;
			if(cnt >= 1) {
			  ows << L")";
			}
		  } while(e || cnt != 0);
		  DragonSentence* ssp = new DragonSentence();
		  PartialAnalyze(ssp, ows.str());
		  DragonFuncPart tp(ssp, false, true, false);
		  sp->push_back(tp);
		}
		break;
		
	  /* Selection [.A./.B./.C.] */
	  case L'[':
		if(escaped) {
		  DragonFuncPart tp(L"[");
		  sp->push_back(tp);
		} else {
		  std::wstring tws;
		  std::list<std::wstring> vtws;
		  std::wostringstream ows;
		  bool e = false; // escaped.
		  unsigned int cnt = 1;
		  std::wstring::size_type t = 0;
		  do {
			e = false;
			std::getline(iws, tws, L'/');
			if(iws.eof()) {
			  throw DragonExcept(&msgNoClosingSquareBracket);
			}
			if(tws[tws.length() - 1] == L'\\') {
			  e = true;
			}
			for(std::wstring::size_type i = tws.find(L']', 0); i != std::wstring::npos; i = tws.find(L']', i + 1)) {
			  if(i == 0 || tws[i - 1] != L'\\') {
				cnt--;
				t = i;
			  }
			}
			if(cnt != 0) {
			  for(std::wstring::size_type i = tws.rfind(L'['); i == std::wstring::npos; i = tws.rfind(L'[', i - 1)) {
				if(i == 0 || tws[i - 1] != L'\\') {
				  if(i < t) {
					throw DragonExcept(&msgSingleSelection);
				  }
				  cnt++;
				}
			  }
			}
			if(e) {
			  if(cnt > 1) {
				tws += L"\\";
			  }
			  tws += L"/";
			}
			if(cnt > 1) {
			  ows << tws;
			} else if(cnt == 1) {
			  ows << tws;
			  vtws.push_back(ows.str());
			  ows.str(L"");
			} else {
			  ows << tws.substr(0, t);
			  vtws.push_back(ows.str());
			}
		  } while(e || cnt != 0);
          vtws.sort();
          DragonSentence* ts = new DragonSentence(vtws.begin(), vtws.end());
          DragonFuncPart tp(ts, true, false, false);
          sp->push_back(tp);
		}
		break;
		
	  /* Order change */
	  case L'{':
		break;
		
	  /* Universal */
	  case L'*':
		break;
		
	  /* Repeat */
	  case L'<':
		break;
		
	  /* Can be Word break */
	  case L' ':
		break;
	  
	  /* Must be word break */
	  case L',':
		break;
	  
	  default:
		break;
	}
  }
}

const dragon::DragonSentence& dragon::DragonFuncInfo::Analyze(const std::wstring& command_sentence) {
  if(command_sentence == L"") {
	throw DragonExcept(&msgEmptyCommandSentence);
  }
  
  _master_parts.clear();
  
  PartialAnalyze(&_master_parts, command_sentence);
  
  MakeName();
  return _master_parts;
}

const std::wstring& dragon::DragonFuncInfo::PartialMakeName(dragon::DragonSentence::const_iterator p) {
  if(p->getToken() != L"*" && p->getToken() != L"") {
	return p->getToken();
  } else if(p->getSubParts() != NULL) {
	if(p->getSelection()) {
	  return PartialMakeName(p->getSubParts()->begin());
	} else {
	  return PartialMakeName(p->getSubParts());
	}
  }
  static std::wstring ret = L"";
  return ret;
}

const std::wstring& dragon::DragonFuncInfo::PartialMakeName(const dragon::DragonSentence* sp) {
  dragon::DragonSentence::const_iterator p;
  static std::wstring ret = L"";
  for(p = sp->begin(); p != sp->end(); p++) {
	if(p->getToken() != L"*" && p->getToken() != L"") {
	  ret += p->getToken();
	} else if(p->getSubParts() != NULL) {
	  if(p->getSelection()) {
		ret += PartialMakeName(p->getSubParts()->begin());
	  } else {
		ret += PartialMakeName(p->getSubParts());
	  }
	}
  }
  return ret;
}

const std::wstring& dragon::DragonFuncInfo::MakeName() {
  _command_name = L"";
  
  dragon::DragonSentence::const_iterator dsp;
  
  for(dsp = _master_parts.begin(); dsp != _master_parts.end(); dsp++) {
	if(dsp->getToken() != L"*" && dsp->getToken() != L"") {
	  _command_name += dsp->getToken();
	} else if(dsp->getSubParts() != NULL) {
	  if(dsp->getSelection()) {
		_command_name += PartialMakeName(dsp->getSubParts()->begin());
	  } else {
		_command_name += PartialMakeName(dsp->getSubParts());
	  }
	}
  }
  return _command_name;
}
